﻿//Copyright (C) 2009  Jaco (ScionBot.com)

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using Scion.Core;
using Scion.Library.Antiban;
using Scion.Library.Underlying;
using System.Threading;

namespace Scion.Library.RS
{
    /// <summary>
    /// Walks a path of tiles specified.
    /// </summary>
    public class TilePath : Determine
    {
        Superior super;

        /// <summary>
        /// The tiles to walk on.
        /// </summary>
        public Tile[] tiles;

        /// <summary>
        /// Walks a path of tiles specified.
        /// </summary>
        /// <param name="s">The bot's instance.</param>
        /// <param name="tile">The array of tiles to walk over.</param>
        public TilePath(Superior s, Tile[] tile)
        {
            super = s;
            tiles = tile;
        }

        /// <summary>
        /// Walks to a specific tile in the tile array.
        /// </summary>
        /// <param name="index">The index of the tile.</param>
        /// <param name="offset">The offset of the mouse click (pixels).</param>
        /// <returns>Returns true if the function succeeded.</returns>
        public bool TileWalkTo(int index, int offset)
        {
            Tile t = tiles[index];
            return WalkTo(t, offset);
        }

        /// <summary>
        /// Walks to a specific tile in the tile array.
        /// </summary>
        /// <param name="index">The index of the tile.</param>
        /// <param name="offset">The offset of the mouse click (pixels).</param>
        public void WalkTo(int index, int offset)
        {
            TileWalkTo(index, offset);
        }

        private void WaitForFlag(Tile t)
        {
            do
                super.Sleep(new SleepRandom(1000, 1100));
            while (super.DistanceToTile(t) > 6 || super.IsMoving() || super.IsLoading() || super.ConnectionLost());
            super.Sleep(new SleepRandom(1000, 2000));
        }

        private bool LogPathFail(Tile t)
        {
            super.Log.WriteLine("Tile " + t.x.ToString() + ", " + t.y.ToString() + " is not reachable!");
            return false;
        }

        private bool WalkTo(Tile t, int offset)
        {
            Point loc = super.Stub.botReflection.ComputeMinimapTile(t);
            if (Decide(loc))
            {
                bool res = super.Mouse.MoveClick(new PointRandom(loc.X, loc.Y, offset), true);
                WaitForFlag(t);
                return res;
            }
            else
                return LogPathFail(t);
        }

        private bool PathWalkTo(Tile t, int offset)
        {
            Point loc = super.Stub.botReflection.ComputeMinimapTile(t);
            if (Decide(loc))
            {
                return super.Mouse.MoveClick(new PointRandom(loc.X, loc.Y, offset), true);
            }
            else
                return LogPathFail(t);
        }

        private bool PerformWait(int i, int add)
        {
            //Sleeps the bot until ismoving is updated.
            super.Sleep(new SleepRandom(1000, 1100));
            //While the player is moving, we check if the next minimap spot is available.
            while (super.IsMoving() || super.IsLoading() || super.ConnectionLost())
            {
                //If the next tile is located, return wait procedure.
                if (super.Reflection.PossibleMinimapClick(tiles[i + add]) && !super.IsLoading() && !super.ConnectionLost())
                    return true;
                //Sleep to non-kill CPU
                super.Sleep(new SleepRandom(600, 800));
            }
            //We must be stuck -.-. We're not moving and the next tile was not located.
            //Try once more.
            if (super.Reflection.PossibleMinimapClick(tiles[i + add]))
                return true;
            else return false;
        }

        private bool CanReach(Tile t)
        {
            return super.Reflection.PossibleMinimapClick(t);
        }

        /// <summary>
        /// Walks to the end of the path.
        /// </summary>
        /// <param name="offset">The offset of the mouse click (pixels).</param>
        /// <returns>Returns true if the function succeeded</returns>
        public bool TileWalkEnd(int offset)
        {
            if (super.OnTile(tiles[tiles.Length - 1]))
                return true;
            //start in reverse and find the farthest tile the bot can click.
            int indexFound = -1;
            for (int x = tiles.Length - 1; x != -1; x--)
            {
                if (CanReach(tiles[x]))
                {
                    indexFound = x;
                    break;
                }
            }
            if (indexFound == -1)
                return LogPathFail(tiles[tiles.Length - 1]);
            else if (indexFound == (tiles.Length - 1))
                if (super.OnTile(tiles[indexFound]))
                    return true;
            //start from that tile and continue to walk to end of path.
            for (int i = indexFound; i < tiles.Length; i++)
            {
            TryAgain:
                super.Script.FailProtect();
                //if we're landing on last tile next, make sure performwait is not executed.
                if (i == (tiles.Length - 1))
                {
                    PathWalkTo(tiles[i], 0);
                    WaitForFlag(tiles[i]);
                    return true;
                }
                if (CanReach(tiles[i])) PathWalkTo(tiles[i], offset); 
                //Wait, If the outcome is false, we'll try to move to the following tile again.
                if (!PerformWait(i, 1))
                    goto TryAgain;
            }
            return true;
        }

        /// <summary>
        /// Walks to the end of the path.
        /// </summary>
        /// <param name="offset">The offset of the mouse click (pixels).</param>
        public void WalkToEnd(int offset)
        {
            TileWalkEnd(offset);
        }

        /// <summary>
        /// Walks to the start of the tile path.
        /// </summary>
        /// <param name="offset">The offset of the mouse click (pixels).</param>
        /// <returns>Returns true if the function succeeded</returns>
        public bool TileWalkStart(int offset)
        {
            if (super.OnTile(tiles[0]))
                return true;
            int indexFound = -1;
            for (int x = 0; x < tiles.Length; x++)
            {
                if (CanReach(tiles[x]))
                {
                    indexFound = x;
                    break;
                }
            }
            if (indexFound == -1)
                return LogPathFail(tiles[0]);
            else if (indexFound == 0)
                if (super.OnTile(tiles[indexFound]))
                    return true;
            for (int i = indexFound; i != -1; i--)
            {
            TryAgain:
                super.Script.FailProtect();
                if (i == 0)
                {
                    PathWalkTo(tiles[i], 0);
                    WaitForFlag(tiles[i]);
                    return true;
                }
                if (CanReach(tiles[i])) PathWalkTo(tiles[i], offset); 
                    PathWalkTo(tiles[i], offset);
                if (!PerformWait(i, -1))
                    goto TryAgain;
            }
            return true;
        }

        /// <summary>
        /// Walks to the start of the tile path.
        /// </summary>
        /// <param name="offset">The offset of the mouse click (pixels).</param>
        public void WalkToStart(int offset)
        {
            TileWalkStart(offset);
        }
    }
}
